package ar.edu.itba.pod.legajo45129.simul.communication;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.log4j.Logger;

import ar.edu.itba.pod.simul.communication.ClusterCommunication;
import ar.edu.itba.pod.simul.communication.Message;
import ar.edu.itba.pod.simul.communication.MessageListener;

public class ClusterCommunicationImpl implements ClusterCommunication, Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private static final Logger logger = Logger.getLogger(ClusterCommunicationImpl.class);
	
	private ConnectionManagerImpl connectionManager;
	private MessageListener messageListener;

	
	Set<Message> sentMessages = new TreeSet<Message>();

	public ClusterCommunicationImpl(ConnectionManagerImpl connectionManager) throws RemoteException {
		UnicastRemoteObject.exportObject(this, 0);
		this.connectionManager = connectionManager;
		this.messageListener = new MessageListenerImpl();
		
	}

	@Override
	public void broadcast(Message message) throws RemoteException {
		List<String> corruptNodes = new ArrayList<String>();
		if (!sentMessages.contains(message)) {
			sentMessages.add(message);

			ClusterAdministrationImpl cmi = (ClusterAdministrationImpl)this.connectionManager.getClusterAdmimnistration();
			Iterator<String> nodes = cmi.getConnectedNodes();
			while (nodes.hasNext()) {
				String node = nodes.next();

				if (!message.getNodeId().equals(node)) {
					logger.info("broadcasting to " + node);
					send(message, node);
					try {
						
						this.connectionManager.getConnectionManager(node).getGroupCommunication().broadcast(message);
					} catch (Exception e) {
						logger.error("error getting the connection manager of node: " + node, e);
						logger.warn("eliminate from the knowns node list");
						corruptNodes.add(node);
					}

				}

			}
			
			cmi.eliminateNodes(corruptNodes);

			messageListener.onMessageArrive(message);

		}
	}

	@Override
	public MessageListener getListener() throws RemoteException {
		return this.messageListener;
	}

	@Override
	public boolean send(Message message, String nodeId) throws RemoteException {
		if (connectionManager.getNodeId().toString().equals(nodeId)) {
			return connectionManager.getGroupCommunication().getListener().onMessageArrive(message);
		}
		
		try {
			ClusterCommunication clusterCommunication = connectionManager.getConnectionManager(nodeId).getGroupCommunication();
			return clusterCommunication.getListener().onMessageArrive(message);
			
		} catch (Exception e) {
			logger.error("error getting the connection manager of node: " + nodeId);			
			return false;
		}
		
	}

}
